Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context / implicit params to synthetics #13279

Closed
wants to merge 54 commits into from

Conversation

tanishiking
Copy link
Member

Rebased version of: tanishiking#2

#13135
Based on: #12885

This PR adds synthetics (for implicit-param and context-param application) to SemanticDB.

Questions

Added synthetics

implicit / context params application

def foo(x: Int)(using Int) = ???
def m(using x: Int) = foo(0)

// synthetics
def m(using x: Int) = foo(0)<<(x)>>
Synthetic(
  <foo(0)>,
  ApplyTree(
    OriginalTree(<foo(0)>),
    List(
      IdTree(<x>),
    )
  )
)

Same with Implicit parameters synthetics in Scala2 https://scalameta.org/docs/semanticdb/specification.html#synthetic-1

anonymous context params

def foo(using Int) = ???
 
// synthetic
def foo(using x$1: Int) = ???
Synthetic(
  <Int>,
  IdTree(<x$1>)
)

(Maybe range should be between using and Int, instead of overlaps with Int).

anonymous given value

given Int = 1

// synthetic
given given_Int: Int = 1
Synthetic(
  <given Int = 1>,
  IdTree(<given_Int>)
)

tanishiking and others added 30 commits August 11, 2021 03:51
To construct symbol table before referring the ParamRef
it's required to traverse the child trees before registering the symbol.
Previously, there were the risks that localIdx receives the symbol
that doens't have `span` because `TypeOps` generates dummy symbols for
refinements in `RefinedType` and `RecType`.
However, with the following commits,
scala@7dd91ad
scala@226e26f
now we never generate dummy symbol in `TypeOps`.
So re-insert the assertion we deleted when copying this method from
`ExtractSemanticDB`.
see: https://github.com/lampepfl/dotty/pull/12885/files#r658150249
For wildcard type `C[_ <: T]`, it's internal type representation will be
`AppliedType(TypeBounds(lo = <Nothing>, hi = <T>))`.

As scalameta for Scala2 does, we'll convert the wildcard type to
`ExistentialType(TypeRef(NoPrefix, C, <local0>), Scope(hardlinks = List(<local0>)))`
where `<local0>` has
- display_name: "_" and,
- signature: type_signature(..., lo = <Nothing>, hi = <T>)

See:
https://github.com/lampepfl/dotty/pull/12885/files#r663797616
https://scalameta.org/docs/semanticdb/specification.html#type-2

Now, when we compile the following Scala program to semanticdb

```scala
class Wildcards {
  def e1: List[_ <: Int] = ???
}
```

The semanticdb's SymbolInformation for `e1` looks like:

From scala3
```
  symbols {
    symbol: "advanced/Wildcards#e1()."
    kind: METHOD
    display_name: "e1"
    language: SCALA
    signature {
      value_signature {
        tpe {
          by_name_type {
            tpe {
              existential_type {
                tpe {
                  type_ref {
                    symbol: "scala/collection/immutable/List#"
                    type_arguments {
                      type_ref {
                        symbol: "local0"
                      }
                    }
                  }
                }
                declarations {
                  hardlinks {
                    symbol: "local0"
                    kind: TYPE
                    display_name: "_"
                    language: SCALA
                    signature {
                      type_signature {
                        type_parameters {
                        }
                        lower_bound {
                          type_ref {
                            symbol: "scala/Nothing#"
                          }
                        }
                        upper_bound {
                          type_ref {
                            symbol: "scala/Int#"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
```

On the other hand, generated from scalameta's metac with scalac 2.13.6

```sh
$ metac --version
Scala compiler version 2.13.6 -- Copyright 2002-2021, LAMP/EPFL and Lightbend, Inc.
```

```
  symbols {
    symbol: "advanced/Wildcards#e()."
    kind: METHOD
    display_name: "e"
    language: SCALA
    signature {
      method_signature {
        type_parameters {
        }
        return_type {
          existential_type {
            tpe {
              type_ref {
                symbol: "scala/package.List#"
                type_arguments {
                  type_ref {
                    symbol: "local0"
                  }
                }
              }
            }
            declarations {
              hardlinks {
                symbol: "local0"
                kind: TYPE
                properties: 4
                display_name: "_"
                language: SCALA
                signature {
                  type_signature {
                    type_parameters {
                    }
                    lower_bound {
                      type_ref {
                        symbol: "scala/Nothing#"
                      }
                    }
                    upper_bound {
                      type_ref {
                        symbol: "scala/Int#"
                      }
                    }
                  }
                }
                access {
                  public_access {
                  }
                }
              }
            }
          }
        }
      }
    }
    access {
      public_access {
      }
    }
  }
```
For example:
`def foo(x: T)(y: T): T` and for `<y>.owner.info` would be like
`MethodType(...<x>, resType = MethodType(...<y>, resType = <T>))`.
(Let's say the outer `MethodType` "outer", and `MethodType` who is
`resType` of outer "inner")

Before this commit, we register <y> to the symbol table with
`(<y>, outer)`, which should be `(<y>, inner)`.

For such a nested method signature, we have to find the "actual" binder
for parameters and register them to the symbol table.
When the prefix of SingleType is Type.Emtpy, we had been printing them
as `<?>.sym.type`, but it should be `sym.type`
Co-authored-by: Jamie Thompson <bishbashboshjt@gmail.com>
Co-authored-by: Jamie Thompson <bishbashboshjt@gmail.com>
By combining two type param clause into one
For example,
```scala
trait SpecialRefinement {
  def pickOne(as: String*): Option[Any]
}

class PickOneRefinement_1[S <: SpecialRefinement { def pickOne(as: String*): Option[String] }] {
  def run(s: S, as: String*): Option[String] = s.pickOne(as:_*)
}
```

In the typed AST, the SpecialRefinement is inside of the TypeTree and
cannot access to the refinement (pickOne) from tree, therefore we can't
register the symbol of pickOne to symbol table.
In this case, fallback to `Type.member` to find the symbol of `pickOne`.
instead, create a proxy data that will be converted to semanticdb symbol
who represents wildcard symbol
Because there could be an unexpected types for incompilable sources
and if we show the warning for uncompilable sources, it could be confusing.
Support following synthetics
- implicit and context parameters application
- Anonymous context param name
- Invented given

Also convert TypeVar to stripped type, which may appear in synthetics
@tanishiking
Copy link
Member Author

Sorry I picked wrong origin branch 🙇

@tanishiking tanishiking deleted the synthetic-context-params branch August 11, 2021 06:40
@tanishiking tanishiking restored the synthetic-context-params branch August 11, 2021 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant